#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "main.h"
#include "keywordMatch.h"
#include "labelHandlers.h"


int getValue (char * errorMsg, char * writeBuffer, struct labelManager * lManager, int allowTypes,
              int * returnType, void * returnValue, int * returnLength, bool offset)
{
    if (allowTypes & INTYPE_REGISTER) {
        int mRegister = matchRegister (writeBuffer);
        if (mRegister != RG_INVALID) {
            *returnType = INTYPE_REGISTER;
            *((int*)returnValue) = mRegister;
            return 0;
        }
    }
    if (allowTypes & INTYPE_IMMEDIATE) {
        if (isNumber (writeBuffer)) {
            int temp;
            sscanf (writeBuffer, "%d", &temp);
            *returnType = INTYPE_IMMEDIATE;
            *((int*)returnValue) = temp;
            return 0;
        }
    }
    if (allowTypes & INTYPE_ASCII) {
        if (isAsciiString (writeBuffer)) {
            char temp[MAX_STRING_LEN];
            int length = getAsciiString (writeBuffer, temp);
            *((char**)returnValue) = malloc (sizeof(char) * (length + 1));
            strcpy (*((char**)returnValue), temp);
            *returnType = INTYPE_ASCII;
            *returnLength = length;
            return 0;
        }
    }
    if (allowTypes & INTYPE_LABEL_LABEL) {
        int lPos = findLabelByName (lManager, writeBuffer);
        if (lPos != -1) {
            if (lManager->labels[lPos].type == LABEL_LABEL) {
                *returnType = INTYPE_LABEL_LABEL;
                *((int*)returnValue) = lManager->labels[lPos].value;
                return 0;
            }
        }
    }
    if (allowTypes & INTYPE_LABEL_ARRAY) {
        int lPos = findLabelByName (lManager, writeBuffer);
        if (lPos != -1) {
            if (lManager->labels[lPos].type == LABEL_ARRAY) {
                *returnType = INTYPE_LABEL_ARRAY;
                *((int*)returnValue) = lManager->labels[lPos].address;
                *returnLength = lManager->labels[lPos].arrayLen;
                return 0;
            }
        }
    }
    if (allowTypes & INTYPE_LABEL_BYTE) {
        int lPos = findLabelByName (lManager, writeBuffer);
        if (lPos != -1) {
            if (lManager->labels[lPos].type == LABEL_BYTE) {
                *returnType = INTYPE_LABEL_BYTE;
                *((int*)returnValue) = lManager->labels[lPos].address;
                return 0;
            }
        }
    }
    if (allowTypes & INTYPE_LABEL_IMMEDIATE) {
        if (isNumber (writeBuffer) || isAsciiString (writeBuffer)) {
            int temp;
            if (isNumber (writeBuffer)) {
                sscanf (writeBuffer, "%d", &temp);
            } else if (isAsciiString (writeBuffer)) {
                temp = writeBuffer[1];
            }
            int lPos = findImmediateLabelByValue (lManager, temp);
            if (lPos != -1) {
                *returnType = INTYPE_LABEL_IMMEDIATE;
                *((int*)returnValue) = lManager->labels[lPos].address;
                return 0;
            }
        }
    }
    if (allowTypes & INTYPE_DATA_ADDRESS) {
        if (isDataAddress (writeBuffer)) {
            int lPos = findLabelByName (lManager, (char*)(writeBuffer + 1));
            if (lPos != -1) {
                *returnType = INTYPE_DATA_ADDRESS;
                *((int*)returnValue) = lManager->labels[lPos].value;
                return 0;
            }
        }
    }
    if (allowTypes & INTYPE_BRACKET) {
        if (isBracket(writeBuffer)) {
            char * temp = malloc (sizeof(char)*MAX_STRING_LEN);
            int mRegister;
            int mSign;
            parseBracket(writeBuffer, &mRegister, &mSign, temp);
            *returnType = INTYPE_BRACKET;
            int * returnArr = malloc (sizeof(int) * 3);
            returnArr[0] = mRegister;
            returnArr[1] = mSign;
            returnArr[2] = (int)temp;
            *((int**)returnValue) = returnArr;
            return 0;
        }
    }
    //error
    return -1;
}

int parseBracket (char * bracket, int * mRegister, int * mSign, char * writeTo) {
    int i = 1;

    char testChar = bracket[i];
    while (testChar != ASCII_RSBRACKET) {
        testChar = bracket[i];
        if (testChar == ASCII_SPACE) {
            i++;
        } else {
            break;
        }
    }
    if (testChar == ASCII_RSBRACKET) {
        //error
        return -1;
    }
    writeTo[0] = bracket[i];
    i++;
    writeTo[1] = bracket[i];
    i++;
    writeTo[2] = 0;
    int mReg = matchRegister(writeTo);;
    if (mReg == RG_INVALID) {
        //error
        return -1;
    }
    *mRegister = mReg;
    testChar = bracket[i];
    while (testChar != ASCII_RSBRACKET) {
        testChar = bracket[i];
        if (testChar == ASCII_SPACE) {
            i++;
        } else {
            break;
        }
    }
    if (bracket[i] == ASCII_PLUS) {
        *mSign = 1;
    } else if (bracket[i] == ASCII_MINUS) {
        *mSign = 2;
    } else if (bracket[i] == ASCII_RSBRACKET) {
        *mSign = 1; //add
        strcpy (writeTo, "0"); //we want to add 0
        return 0;
    } else {
        //error
        return -1;
    }
    i++;
    testChar = bracket[i];
    while (testChar != ASCII_RSBRACKET) {
        testChar = bracket[i];
        if (testChar == ASCII_SPACE) {
            i++;
        } else {
            break;
        }
    }
    int j = 0;
    testChar = bracket[i];
    while (testChar != ASCII_RSBRACKET) {
        testChar = bracket[i];
        writeTo[j] = bracket[i];
        i++;
        j++;
    }
    if (j > 0) {
        writeTo[j-1] = 0;
    } else {
        //error
        return -1;
    }
    return 0;
}


int matchRegister(char * reg) {
    if (!strcmp(reg, "R0")) {
        return RG_R0;
    } else if (!strcmp(reg, "R1")) {
        return RG_R1;
    } else if (!strcmp(reg, "R2")) {
        return RG_R2;
    } else if (!strcmp(reg, "R3")) {
        return RG_R3;
    } else {
        return RG_INVALID;
    }
}

bool isNumber(char * number) {
    int i = 0;
    bool check = true;
    while(true) {
        if (number[i] == 0) {
            return check;
        } else if (!(ASCII_ZERO <= number[i] && number[i] <= ASCII_NINE)) {
            check = false;
            return check;
        }
        i++;
    }
    return check;
}

bool isAsciiString(char * ascii) {
    int i = 1;
    if (ascii[0] == ASCII_APOSTROPHE)
    {
        while(ascii[i] != 0) {
            if(ascii[i] == ASCII_APOSTROPHE) {
                if(ascii[i-1] != ASCII_BACKSLASH) {
                    return true;
                }
            }
            i++;
        }
    }
    return false;
}

int getAsciiString(char* ascii, char* writeTo) {
    int i = 1;
    if (ascii[0] == ASCII_APOSTROPHE) {
        while(ascii[i] != 0) {
            if(ascii[i] == ASCII_APOSTROPHE) {
                if(ascii[i-1] != ASCII_BACKSLASH) {
                    writeTo[i-1] = 0;
                    return i-1;
                }
            }
            writeTo[i-1] = ascii[i];
            i++;
        }
    }
    return 0;
}

bool isDataAddress(char * label) {
    if(label[0] == ASCII_AT) {
        return true;
    }
    return false;
}

bool isBracket(char * bracket) {
    if(bracket[0] == ASCII_LSBRACKET) {
        return true;
    }
    return false;
}

int matchSegment(char * segment)
{
    if(!strcmp(segment, ".data")) {
        return SEG_DATA;
    } else if (!strcmp(segment, ".code")) {
        return SEG_CODE;
    } else {
        return 0;
    }
}

int matchLabelType(char * type)
{
    if(!strcmp(type, "byte")) {
        return LABEL_BYTE;
    } else if(!strcmp(type, "array")) {
        return LABEL_ARRAY;
    } else if(!strcmp(type, "label")) {
        return LABEL_LABEL;
    } else {
        return LABEL_NONLABEL;
    }
}

int matchMnemonic(char * mnemonic)
{
    if (!strcmp(mnemonic, "halt")) {
        return MN_HALT;
    } else if(!strcmp(mnemonic, "add")) {
        return MN_ADD;
    } else if(!strcmp(mnemonic, "sub")) {
        return MN_SUB;
    } else if(!strcmp(mnemonic, "mul")) {
        return MN_MUL;
    } else if(!strcmp(mnemonic, "inc")) {
        return MN_INC;
    } else if(!strcmp(mnemonic, "dec")) {
        return MN_DEC;
    } else if(!strcmp(mnemonic, "beep")) {
        return MN_BEEP;
    } else if(!strcmp(mnemonic, "print")) {
        return MN_PRINT;
    } else if(!strcmp(mnemonic, "printa")) {
        return MN_PRINTA;
    } else if(!strcmp(mnemonic, "printi")) {
        return MN_PRINTI;
    } else if(!strcmp(mnemonic, "mov")) {
        return MN_MOV;
    } else if(!strcmp(mnemonic, "jmp")) {
        return MN_JMP;
    } else if(!strcmp(mnemonic, "jz")) {
        return MN_JZ;
    } else if(!strcmp(mnemonic, "jnz")) {
        return MN_JNZ;
    } else if(!strcmp(mnemonic, "clear")) {
        return MN_CLEAR;
    } else {
        return 0;
    }
}
